import datetime
import os
import sys
import threading
import re
import time

from utils.DbHelper2023 import DbHelper, DB_TYPE_MYSQL


def update_attributes(source, target,attrs=[]):
  upded_attrs = []
  for attr_name in dir(source):
    if not attr_name.startswith('_') and not callable(getattr(source, attr_name)):
      if not attrs or attr_name in attrs:  # 未指定属性列表，或当前属性在列表中，则更新
        if hasattr(target, attr_name) and getattr(source, attr_name):  # 目标对象有该属性，且源对象有值
          setattr(target, attr_name, getattr(source, attr_name))
          upded_attrs.append(attr_name)
  return upded_attrs

class SingletonMeta(type):
  _instance = {}
  _lock = threading.Lock()

  def __call__(cls, *args, **kwargs):
    key = (cls, str(args), frozenset((k, str(v) if isinstance(v, dict) else v) for k, v in kwargs.items()))
    with cls._lock:
      if key not in cls._instance:
        cls._instance[key] = super().__call__(*args, **kwargs)
      return cls._instance[key]

class DbHelperFactory(metaclass=SingletonMeta):
  def __init__(self, db_conf=None, db_type=DB_TYPE_MYSQL):
    self.dbHelper = DbHelper(configFile0=db_conf, db_type=db_type)

  def get_db_helper(self):
    return self.dbHelper


from loguru import logger

getYMDHmsRand = lambda  : int(round(time.time() * 1000))
getYM = lambda: datetime.datetime.now().strftime('%Y%m')

def get_file_md5(file_name):
  import hashlib
  md5 = hashlib.md5()
  with open(file_name, 'rb') as fobj:
    while True:
      data = fobj.read(40960)
      if not data:
        break
      md5.update(data)  # 更新
  return md5.hexdigest()

def convert_to_pinyin(path):
  from pypinyin import pinyin, Style
  # 将路径中的每个字符转换为拼音
  pinyin_list = pinyin(path, style=Style.NORMAL)

  # 将拼音列表拼接成一个字符串
  pinyin_path = ''.join([item[0] for item in pinyin_list])

  return pinyin_path

def add_syspath(sour_file):
  mypath, file_name0 = os.path.split(os.path.realpath(sour_file))
  while True:
    path, file_name = os.path.split(sour_file)
    if file_name == 'src':
      root_path = sour_file
      logger.info(' in %s  , do sys.path.append root_path=%s' % (file_name0, root_path))
      sys.path.append(root_path)
      return True
    sour_file = path

def get_leaf_nodes(obj, parent_key=''):
  """
  递归获取复杂对象的叶子节点的属性名和属性值。

  :param obj: 要遍历的对象
  :param parent_key: 当前属性的父键，用于构建完整的键路径
  :return: 包含叶子节点属性名和属性值的字典
  """
  leaf_nodes = {}

  if isinstance(obj, dict):
    for key, value in obj.items():
      new_key = f"{parent_key}.{key}" if parent_key else key
      leaf_nodes.update(get_leaf_nodes(value, new_key))
  elif isinstance(obj, list):
    for index, value in enumerate(obj):
      new_key = f"{parent_key}[{index}]"
      leaf_nodes.update(get_leaf_nodes(value, new_key))
  else:
    leaf_nodes[parent_key] = obj

  return leaf_nodes


# 全角转半角
def strQ2B(ustring):
  ss = []
  for s in ustring:
    rstring = ""
    for uchar in s:
      inside_code = ord(uchar)
      if inside_code == 12288:
        inside_code = 32
      elif (inside_code >= 65281 and inside_code <= 65374):
        inside_code -= 65248
      rstring += chr(inside_code)
    ss.append(rstring)
  return ''.join(ss)

class Normalization_med_word:
  """ 对一些关键的医学词汇进行归一化处理，如
  主 诉 --> 主诉
  诊 断 --> 诊断
  """
  def __init__(self):
    super().__init__()
    self.patterns = {
      r"(主\s*诉)": "主诉",
      r"(病\s*史)": "病史",
      r"(病\s*情)": "病情",
      r"(病\s*史)": "病史",
      r"(诊\s*断)": "诊断"
    }

  def normalize(self, s):
    import re
    s1 = s
    for pattern, replacement in self.patterns.items():
      s1 = re.sub(pattern, replacement, s1)
    return s1

class Normalization_date:
  def __init__(self):
    super().__init__()
    self.p1 = r'(20\d{2})-(\d{1,2})-(\d{1,2})(\d{1,2}:\d{1,2}:\d{1,2})'  # 2022-01-0112:30:00
    self.p2 = r'(20\d{2})/(\d{1,2})/(\d{1,2})(\d{1,2}:\d{1,2}:\d{1,2})'  # 2022/01/0112:30:00
    self.p3 = r'(20\d{2})-(\d{1,2})-(\d{1,2})(\d{1,2}:\d{1,2})'  # 2022-01-0112:30
    self.p4 = r'(20\d{2})/(\d{1,2})/(\d{1,2})(\d{1,2}:\d{1,2})'  # 2022/01/0112:30
    self.patterns = [self.p1, self.p2, self.p3, self.p4]

  def parse_datetime(self, s):
    import re
    s1 = s
    for pattern in self.patterns:
      s1 = re.sub(pattern, r'\1-\2-\3 \4', s1)
    return s1

def find_all_positions(str1, str2):
  """ 得到字符串 str1 在字符串 str2 中出现的全部位置  ，
  测试用例见 test_find_index_spanning_elements()
    """
  positions = []
  for match in re.finditer(re.escape(str1), str2):
    positions.append(match.start())
  return positions

class Find_index_spanning_elements:
  def __init__(self, array):
    self.array = array
    self.allstr = "".join(array)
    self.positions0 = self.init_positions(array)

  def init_positions(self, array):
    """
    初始化每个元素在字符串中的位置。
    """
    positions = []
    pre_pos = 0
    for val in array:
      positions.append(pre_pos)
      pre_pos += len(val)
    positions.append(sys.maxsize)  # 最后一个元素的位置为无穷大
    return positions

  def try_get_idxs(self,target_len,start_idx):
    total_len = len(self.array[start_idx])
    rtn_idxs = [start_idx]
    for i in range(start_idx+1,len(self.array)):
      if total_len + len(self.array[i]) >= target_len:
        for k in range(start_idx+1,i+1):
          rtn_idxs.append(k)
        break
    return rtn_idxs

  def find_all_idxs_in_arr(self, target):
    """
    找到目标元素在数组中的所有起始索引位置。
    """
    positions = find_all_positions(target, self.allstr)
    rtn_idxs = []
    for pos in positions:
      for idx,pos0 in enumerate(self.positions0):
        if pos0 <= pos < self.positions0[idx + 1]:
          if len(self.array[idx])>=len(target):
            rtn_idxs.append([idx])
          else: # 目标元素长度大于找到的首个数组元素长度，尝试寻找多个数组元素
            rtn_idxs.append(self.try_get_idxs( len(target),idx ))
          break

    return rtn_idxs

def normalization_date(s):
  pattern = r'(\d{4})-(\d{2})-(\d{2})(\d{2}:\d{2}:\d{2})'
  pass

def test1():
  from conf import Config as conf
  dbhelper1 = DbHelperFactory(conf.mysql_conf, DbHelper.DB_TYPE_MYSQL).get_db_helper()
  dbhelper2 = DbHelperFactory(conf.mysql_conf2, DbHelper.DB_TYPE_MYSQL).get_db_helper()
  assert id(dbhelper1) == id(dbhelper2)

def time_count(func):
  import time
  def wrapper(*args, **kwargs):
    start_time = time.time()
    result = func(*args, **kwargs)
    end_time = time.time()
    print(f"函数【{func.__name__}】运行时间为{end_time - start_time}秒")
    return result

  return wrapper


def test_DbHelper_with_pool():
  from conf import Config as conf
  dbhelper1 = DbHelperFactory(conf.mysql_conf, DbHelper.DB_TYPE_MYSQL).get_db_helper()

  @time_count
  def inner1():
    for i in range(100):
      dbhelper1.runSql4All("select count(*) from ocr_task")

  inner1()

def test_normalization_date():
  n = Normalization_date()
  s = """房人体温 37.0 ℃ 氧和血红蛋白分数 96 93-98 %
氧饱和度 99.4↑ 91.9-99.0 % 高铁血红蛋白分数 1.6↑ 0.0-1.5 %
血细胞压积 16.41 40.0-50.0 % 碳氧血红蛋白分数 2 0.-2 %
验者:邱颖 审核者:胡清芳 审核时间:2024-05-0713:6:39
【乙肝三系检查(定量)】 报告时间:[2024/05/0213:15:15]
项目名称 检验结果 参考值 单位 项目名称 检验结果 参考值 单位
乙型肝炎病毒表面抗原 阳性0.08* 阴性<0.05;阳 IU/mL 乙型肝炎病毒e抗体 阳性0.24* 阴性>1.00; S/CO
审核日期:2024-05-0718:32
  """
  s1 = n.parse_datetime(s)
  print(s1)

def test_med_word_normalization():
  n = Normalization_med_word()
  s = "主    诉 ： 乙肝三系检查(定量) 报告时间：2024/05/0213:15:15 项目名称 检验结果 参考值 单位 项目名称 检验结果 参考值 单位 审核日期:2024-05-0718:32 乙型肝炎病毒表面抗原 阳性0.08* 阴性<0.05;阳 IU/mL 乙型肝炎病毒e抗体 阳性0.24* 阴性>1.00; S/CO"
  s1 = n.normalize(s)
  print(s1)

if __name__ == '__main__':
  # test_normalization_date()
  # test_med_word_normalization()
  test_DbHelper_with_pool()
